package ru.ipccenter.jshell.frontend.shell;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


public class InterprocessStreamInternalImpl implements InterprocessStreamInternal {

    protected ShellInternal shell;
    protected InputStream input;
    protected OutputStream output;
    protected boolean error;
    protected Exception exception;
    
    public InterprocessStreamInternalImpl(InputStream input, ShellInternal shell) {

        this.shell = shell;
        this.input = input;
    }
    
    @Override 
    public void setOutput(OutputStream output) {
        
        this.output = output;
    }
    
    @Override
    public void write() {

        setOutput(shell.getOutputSocketStream());
        start();
    }

    @Override
    public void write(String path) throws IOException {
        
        File file = new File(path);
        
        if (!file.canWrite()) {
            throw new IOException(
                    "Can't write to " + file.getCanonicalPath());
        }
        setOutput(new FileOutputStream(file));
        start();
    }
    
    @Override
    public InterprocessStreamInternal exec(String... args) {
       
        return Executor.exec(this, shell, args, null, null);
    }
    
    @Override
    public void run() {

        try {
            while (pumpData()) {};
        } catch (Exception e) {
            error = true;
            exception = e;
            return;
        } finally {
            try {
                output.close();
            } catch (Exception e) {};
        }
    }
   
    @Override
    public void start() {
        
        if (null == input) {
            throw new RuntimeException("Input is null");
        } else if (null == output) {
            throw new RuntimeException("Output is null");
        }
        
        try {
            Thread pumpJob = new Thread(this);
            pumpJob.start();
            pumpJob.join(); 
        } catch (InterruptedException e) {
            error = true;
            exception = e;
        }
        
        if (error) {
            throw new RuntimeException(exception);
        }
    }

    private boolean pumpData() throws IOException {
        
        int data = input.read();
        if (-1 != data) {
            output.write(data);
            return true;
        } else {
            return false;
        }
    }
}
